import cPickle as pickle
import gobject
import random

from net import network, queues
import bits, message
from util import config

SEPERATOR = ':::'

import logging
logger = logging.getLogger('async.control')
class Control:

    def __init__(self, manager, iface, control_port):
        self.__manager = manager
        self.__queue = queues.Queue()
        self.__socket = network.Unicast(iface, control_port, self.__queue, self.__receive)
        self.__tasks = {}

    def __receive(self, data, addr):
        ip, port = addr
        elts = data.split(SEPERATOR)
        command = elts[0]
        logger.debug('Received command: %s',command)
        if command == 'busy?':
            if self.__manager.is_busy():
                response = '__busy__' + SEPERATOR + elts[1]
            else:
                response = '__go__' + SEPERATOR + elts[1]
            self.__queue.put(response, addr)
        elif command == 'header':
            self.__handle_header(addr, *elts[1:])
        elif command == 'retransmit':
            self.__handle_retransmit(addr, *elts[1:])
        elif command == '__busy__':
            tid = int(elts[1])
            task = self.__tasks.get(tid, None)
            if task:
                task.stop()
        elif command == '__go__':
            tid = int(elts[1])
            task = self.__tasks.get(tid, None)
            if task:
                task.go(addr)
        elif command == '__stop__':
            tid = int(elts[1])
            task = self.__tasks.get(tid, None)
            if task:
                task.stop()

    # handlers
    def __handle_header(self, addr, raw_header, tid):
        ip, port = addr
        header = message.Header.from_raw(raw_header)
        logger.debug('Handling incoming header: mid %i', header.mid)

        if not self.__manager.has(header.mid):
            self.__manager.register_incoming(header)
            self.__queue.put('__go__' + SEPERATOR + tid, addr)
        else:
            self.__queue.put('__stop__' + SEPERATOR + tid, addr)

    def __handle_retransmit(self, addr, mid, pickled_target, packed_seqs, tid):
        mid = int(mid)
        msg = self.__manager.get_message(mid)
        presence = pickle.loads(pickled_target)
        seqs = bits.unpack_seqs(packed_seqs)
        logger.debug('Retransmitting %s for mid %i', seqs, mid)
        # TODO: major hack)
        self.__manager.retransmission_queue.put(msg, (presence.ip, presence.meta[config.get_int('async.metas', 'unicast-port-key')]), seqs)

    def send_message(self, msg, target, net_queue):
        task = Control.SendMessage(self.__queue, msg, target, net_queue)
        self.__tasks[task.tid] = task        
        task.connect('stopped', self.__on_task_stopped)
        task.run()

    def request_retransmission(self, header, seqs, target, me):
        task = Control.RequestRetransmission(self.__queue, header, seqs, target, me)
        self.__tasks[task.tid] = task
        task.connect('stopped', self.__on_task_stopped)
        task.run()

    def __on_task_stopped(self, gobject, tid):
        del self.__tasks[tid]

    class Task(gobject.GObject):

        __gsignals__ = {
            'stopped' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                    (gobject.TYPE_INT,))
        }

        def __init__(self, control_queue, target):
            gobject.GObject.__init__(self)
            self.tid = random.randint(0,1000000)
            self._queue = control_queue
            self._target = target
            #self.__max_retries = 2
            #self.__retry_count = 0
            self._step = -1
            self._previous_step = -1

        def run(self):
            self._queue.put('busy?' + SEPERATOR + str(self.tid), self._target.control_addr)
            #self.__retry_timer = gobject.timeout_add(500, self.__retry)
            self.__stop_timer = gobject.timeout_add(1000, self.stop)

        def __retry(self):
            if self._step == self._previous_step:
                self.__retry_count += 1
                if self.__retry_count > self.__max_retries:
                    self.stop()
                    return False
                else:
                    self.run()
                    return True
            
        def stop(self):
            #gobject.source_remove(self.__retry_timer)
            gobject.source_remove(self.__stop_timer)
            self.emit('stopped', self.tid)

        def go(self):
            raise NotImplementedError

    class SendMessage(Task):
        
        def __init__(self, control_queue, msg, target, net_queue):
            Control.Task.__init__(self, control_queue, target)
            self.__msg = msg
            self.__net_queue = net_queue

        def go(self, addr):
            self._previous_step = self._step
            self._step += 1
            if self._step == 0:
                self._queue.put('header' + SEPERATOR + self.__msg.header.raw() + SEPERATOR + str(self.tid), addr)
            elif self._step == 1:
                self.__net_queue.put(self.__msg, self._target.send_addr) # , range(10,self.__msg.header.lsn)) # Use this is you want to force dropped packets
            else:
                raise Exception

    class RequestRetransmission(Task):
        
        def __init__(self, control_queue, header, seqs, target, me):
            Control.Task.__init__(self, control_queue, target)
            self.__header = header
            self.__seqs = seqs
            self.__me = me
    
        def go(self, addr):
            self._previous_step = self._step
            self._step += 1
            self._queue.put('retransmit' + SEPERATOR + str(self.__header.mid) + SEPERATOR + pickle.dumps(self.__me, pickle.HIGHEST_PROTOCOL) + SEPERATOR + bits.pack_seqs(self.__seqs, self.__header.lsn) + SEPERATOR + str(self.tid), addr)
            

gobject.type_register(Control.Task)